package com.example.testdemo.kinetic.sprite.tank.enemy;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.PathModifier;
import org.andengine.entity.modifier.PathModifier.IPathModifierListener;
import org.andengine.entity.modifier.PathModifier.Path;
import org.andengine.entity.sprite.Sprite;
import org.andengine.extension.tmx.TMXLayer;
import org.andengine.extension.tmx.TMXObject;
import org.andengine.extension.tmx.TMXTile;
import org.andengine.opengl.texture.region.ITiledTextureRegion;

import android.util.Log;

import com.example.testdemo.TankGameActivity;
import com.example.testdemo.kinetic.data.DataOfEnemyTank;
import com.example.testdemo.kinetic.interfaces.IPathDrived;
import com.example.testdemo.kinetic.interfaces.IStateChangeable;
import com.example.testdemo.kinetic.sprite.TankSprite;
import com.example.testdemo.kinetic.sprite.bullet.Bullet1;
import com.example.testdemo.kinetic.sprite.tank.player.PlayerBaseTank;
import com.example.testdemo.kinetic.sprite.tank.player.PlayerBaseTank.IOnPositionChangedListener;
import com.example.testdemo.kstatic.sprite.ItemSprite;
import com.example.testdemo.pools.PathPools;
import com.example.testdemo.pools.PathPools.IOnPathReadyListener;
import com.example.testdemo.pools.PathPools.PathRequester;

public abstract class EnemyBaseTank extends TankSprite implements
        IStateChangeable, IPathDrived {
	public final static String BASE_NAME = EnemyTank1.class.getSimpleName();
	
	private final String TAG = BASE_NAME;

	public DataOfEnemyTank mData = null;

	protected PlayerBaseTank mPlayer = null;

	// auto fire
	private boolean mAutoFire = false;
	private int mFireSpeedCounter = 0;

	// state changeable
	final float PLAYER_POS_CHANGE_RANGE = 32f;
	final int PLAYER_FOUNDABLE_DISTANCE = 6;
	int mState = STATE_STANDBY;
	float mPlayerPos[] = new float[] { 0, 0 };

	// auto move
	private int mPathRequestCounter = 0; // 1 update per seconds
	private Random mRandom = new Random(System.currentTimeMillis());
	private Path mPath = null;
	private boolean mPathModifierWorking = false;

	public EnemyBaseTank(int whichScene, String pName, float pX, float pY,
	        ITiledTextureRegion pTiledTextureRegion, TankGameActivity pActivity) {
		super(whichScene, pName, pX, pY, pTiledTextureRegion, pActivity);
		mData = new DataOfEnemyTank(DataOfEnemyTank.STRENGTH_1,
		        DataOfEnemyTank.DEFENCE_1, DataOfEnemyTank.MOVE_SPEED_1,
		        DataOfEnemyTank.FIRE_SPEED_1);
		mPlayer = (PlayerBaseTank) mOnScene.getChildByTag(mOnScene
		        .getSpriteTag().PLAYER_TANK_START);
		mPlayer.registerListenPostionChanged(mPlayerPosChangedListener);
	}

	@Override
	protected void pickItem(ItemSprite item) {
		item.setVisible(false);
	}

	@Override
	public void resetSpriteData() {
		mData.reset();
	}

	public void setBirthTile(int birthTileIndex) {
		if (birthTileIndex < 0
		        || birthTileIndex > mOnScene.getSceneResources().mObjTankList
		                .size()) {
			Log.d(TAG, "error birth tile index : " + birthTileIndex);
			return;
		}

		final TMXObject objTank = mOnScene.getSceneResources().mObjTankList
		        .get(birthTileIndex);
		setPosition(objTank.getX(), objTank.getY());
	}

	public void setAutoFire(boolean auto) {
		mAutoFire = true;
	}

	public void fire() {
		final Bullet1 bullet = mBulletPools.getOneBullet(this);
		if (bullet == null) {
			return;
		}

		if (mOnDestroyTargetListener != null) {
			bullet.registerDestroyTargetListener(NAME, mOnDestroyTargetListener);
		}

		fireAnimation();
		bullet.fire(this);
	}

	@Override
	protected void onManagedUpdate(float pSecondsElapsed) {
		super.onManagedUpdate(pSecondsElapsed);

		if (isVisible()) {
			if (++mPathRequestCounter >= TankGameActivity.FPS) {
				mPathRequestCounter = 0;
				requestPath();
			}

			// auto fire
			if (mAutoFire) {
				if (++mFireSpeedCounter >= mData.getFireSpeedCounter()) {
					fire();
					mFireSpeedCounter = 0;
				}
			}
		} else {
			clearEntityModifiers();
			mPathModifierWorking = false;
		}
	}

	// IStateChangeable

	protected IOnPositionChangedListener mPlayerPosChangedListener = new IOnPositionChangedListener() {
		@Override
		public void onPositionChanged(float curX, float curY) {
			if (Math.abs(mPlayerPos[0] - curX) < PLAYER_POS_CHANGE_RANGE
			        && Math.abs(mPlayerPos[1] - curY) < PLAYER_POS_CHANGE_RANGE) {
				return;
			}

			mPlayerPos[0] = curX;
			mPlayerPos[1] = curY;

			// when player is destroyed, all tanks stop finding a target.
			if (mPlayer == null || !mPlayer.isVisible()) {
				return;
			}

			final TMXLayer layer = mOnScene.getSceneResources().mMap
			        .getTMXLayers().get(0);
			if (layer == null) {
				return;
			}

			TMXTile playertOnTile = layer.getTMXTileAt(
			        mPlayer.getX() + mPlayer.getWidth() / 2, mPlayer.getY()
			                + mPlayer.getHeight() / 2);

			TMXTile tankOnTile = layer.getTMXTileAt(getX() + getWidth() / 2,
			        getY() + getHeight() / 2);

			if (Math.abs(tankOnTile.getTileColumn()
			        - playertOnTile.getTileColumn())
			        + Math.abs(tankOnTile.getTileRow()
			                - playertOnTile.getTileRow()) <= PLAYER_FOUNDABLE_DISTANCE) {
				setState(STATE_SEARCH_PLAYER);
			} else {
				setState(STATE_RANDOM_MOVE);
			}
		}
	};

	@Override
	public void setState(int state) {
		if (state == mState) {
			return;
		}
		mState = state;
	}

	@Override
	public void onStateChanged() {
	}

	// IPathDrived
	private void requestPath() {
		if (!mPathModifierWorking) {
			TMXTile toTile = null;
			switch (mState) {
			case STATE_RANDOM_MOVE:
				final int groundCount = mOnScene.getSceneResources().mTileGroundList
				        .size();
				toTile = mOnScene.getSceneResources().mTileGroundList.get(Math
				        .abs(mRandom.nextInt() % groundCount));
				reqeustPath(this, toTile, PathPools.TARGET_RANDOM_GROUND);
				break;
			case STATE_SEARCH_PLAYER:
				toTile = mOnScene.getSceneResources().mMap
				        .getTMXLayers()
				        .get(0)
				        .getTMXTileAt(mPlayer.getX() + mPlayer.getWidth() / 2,
				                mPlayer.getY() + mPlayer.getHeight() / 2);
				reqeustPath(this, toTile, PathPools.TARGET_PLAYER);
				break;
			}
		}
	}

	@Override
	public void reqeustPath(Sprite sprite, TMXTile toTile, int pathTarget) {
		PathPools.PathRequester requester = null;

		// Generate wall list
		List<Sprite> wallList = new ArrayList<Sprite>();
		for (int i = mOnScene.getSpriteTag().WALL_START; i <= mOnScene
		        .getSpriteTag().WALL_END; i++) {
			Sprite wall = (Sprite) mOnScene.getChildByTag(i);
			if (wall == null || !wall.isVisible()) {
				continue;
			} else {
				wallList.add(wall);
			}
		}

		// Generate obstacle list
		List<Sprite> obstacleList = new ArrayList<Sprite>();
		for (int i = mOnScene.getSpriteTag().OBSTACLE_START; i <= mOnScene
		        .getSpriteTag().OBSTACLE_END; i++) {
			Sprite obstacle = (Sprite) mOnScene.getChildByTag(i);
			if (obstacle == null || !obstacle.isVisible()) {
				continue;
			} else {
				obstacleList.add(obstacle);
			}
		}

		requester = new PathRequester(sprite, toTile, wallList, obstacleList,
		        pathTarget, mPathListener);
		PathPools.getInstances().addPathRequester(requester);
	}

	@Override
	public void setPath(Path path) {
		mPath = path;
	}

	@Override
	public void startMove() {
		clearEntityModifiers();
		registerEntityModifier(new PathModifier(mPath.getLength()
		        / mData.getMoveSpeed(), mPath, mPathModifierListener));
	}

	@Override
	public void stopMove() {
		clearEntityModifiers();
		mPathModifierWorking = false;
	}

	private IOnPathReadyListener mPathListener = new IOnPathReadyListener() {

		@Override
		public void onPathReady(int pathType, Path path) {
			if (!mPathModifierWorking) {
				setPath(path);
				startMove();
			}
		}
	};

	private IPathModifierListener mPathModifierListener = new IPathModifierListener() {

		@Override
		public void onPathStarted(PathModifier pPathModifier, IEntity pEntity) {
			mPathModifierWorking = true;
		}

		@Override
		public void onPathFinished(PathModifier pPathModifier, IEntity pEntity) {
			mPathModifierWorking = false;
		}

		@Override
		public void onPathWaypointStarted(PathModifier pPathModifier,
		        IEntity pEntity, int pWaypointIndex) {
			if (pWaypointIndex > 0) {
				final float x[] = pPathModifier.getPath().getCoordinatesX();
				final float y[] = pPathModifier.getPath().getCoordinatesY();
				float dir = judgeDirection(x[pWaypointIndex - 1],
				        y[pWaypointIndex - 1], x[pWaypointIndex],
				        y[pWaypointIndex]);
				setRotation(dir);
			}
		}

		@Override
		public void onPathWaypointFinished(PathModifier pPathModifier,
		        IEntity pEntity, int pWaypointIndex) {
		}
	};

	private float judgeDirection(float preX, float preY, float curX, float curY) {
		if (curX - preX == 0) {
			if (curY - preY > 0) {
				return EnemyTank1.FACE_DOWN;
			} else {
				return EnemyTank1.FACE_UP;
			}
		} else if (curY - preY == 0) {
			if (curX - preX > 0) {
				return EnemyTank1.FACE_RIGHT;
			} else {
				return EnemyTank1.FACE_LEFT;
			}
		}
		return EnemyTank1.FACE_DOWN;
	}

}
